﻿//$LastChangedDate: 2012-02-16 15:49:15 +0100 (do, 16 feb 2012) $
//$LastChangedRevision: 7 $
//$LastChangedBy: jan-willem $

using System;

namespace TLD_Vision
{
    public struct HSVValue
    {
        public double hue;
        public double saturation;
        public double value;
    };

    public struct HSLValue
    {
        public double hue;
        public double saturation;
        public double lightness;
    };
    
    public class Color
    {
        // Formula derived from description on http://en.wikipedia.org/wiki/HSV_color_space
        // Expects r,g,b values on a scale of 0-255
        // Returns Hue on a scale of 0 - 1
        public static double GetHue(byte r, byte g, byte b) 
        {
            double R = r / 255.0, G = g / 255.0, B = b / 255.0;
            double max = Math.Max(R, Math.Max(G, B)), min = Math.Min(R, Math.Min(G, B));
            double chroma = max - min;

            double hue;
            if (max == min) {
                hue = 0;
            }
            else if (max == R)
            {
                hue = (G - B) / chroma + (G < B ? 6 : 0);
            }
            else if (max == G)
            {
                hue = (B - R) / chroma + 2;
            }
            else
            {
                hue = (R - G) / chroma + 4;
            }

            return hue / 6;
        }

        // Calculates HSV values.
        // Expects r,g,b on scale of 0-255
        // Returns HSV on scale of 0-1
        public static HSVValue RGBtoHSV(byte r, byte g, byte b)
        {
            HSVValue hsv;
            double R = r / 255.0, G = g / 255.0, B = b / 255.0;
            double max = Math.Max(R, Math.Max(G, B)), min = Math.Min(R, Math.Min(G, B));
            double chroma = max - min;

            hsv.hue = GetHue(r, g, b);
            hsv.value = max;
            hsv.saturation = chroma == 0 ? 0 : chroma / hsv.value;

            return hsv;
        }

        // Calculates HSL values.
        // Expects r,g,b on scale of 0-255
        // Returns HSL on scale of 0-1
        public static HSLValue RGBtoHSL(byte r, byte g, byte b)
        {
            HSLValue hsl;
            double R = r / 255.0, G = g / 255.0, B = b / 255.0;

            double max = Math.Max(R, Math.Max(G, B)), min = Math.Min(R, Math.Min(G, B));
            hsl.lightness = (max + min) / 2;

            if (max == min)
            {
                // Hue is actually undefined for chroma = 0, but using 0
                // for convenience.
                hsl.hue = hsl.saturation = 0; 
            }
            else
            {
                double chroma = max - min;
                hsl.saturation = hsl.lightness > 0.5 ? chroma / (2 - max - min) : chroma / (max + min);
                hsl.hue = GetHue(r, g, b);
            }

            return hsl;
        }

        public static double GetLightness(byte r, byte g, byte b)
        {
            double R = r / 255.0, G = g / 255.0, B = b / 255.0;
            double max = Math.Max(R, Math.Max(G, B)), min = Math.Min(R, Math.Min(G, B));
            return (max + min) / 2;
        }

        public static double HueToRGB(double p, double q, double t) {
            if (t < 0) t += 1;
            if (t > 1) t -= 1;
            if (t < 1 / 6) return p + (q - p) * 6 * t;
            if (t < 1 / 2) return q;
            if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
            return p;
        }

        public static byte[] HSLtoRGB(HSLValue hsl)
        {
            double r, g, b;
            if (hsl.saturation == 0)
            {
                r=g=b= hsl.lightness;
            } else {
                double q = hsl.lightness < 0.5 ? hsl.lightness * (1 + hsl.saturation) : hsl.lightness + hsl.saturation - hsl.lightness * hsl.saturation;
                double p = 2 * hsl.lightness - q;

                r = HueToRGB(p, q, hsl.hue + 1 / 3);
                g = HueToRGB(p, q, hsl.hue);
                b = HueToRGB(p, q, hsl.hue - 1 / 3);
            }

            return new byte[] { (byte)(r * 255), (byte)(g * 255), (byte)(b * 255) };
        }
    }
}
